﻿/// ****************************************************************************
/// Author:	        Robin Zhu
/// Create Date:	2012-9-11
/// Purpose: 		对象扩展方法类，增加可用于普通对象的方法
/// ****************************************************************************
/// Modify By		Date			Remark
/// Robin           2013-2-1        防止对只读属性赋值
/// ****************************************************************************

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace RB.Utility
{
    /// <summary>
    /// 对象扩展方法类
    /// </summary>
    public static class ObjectExtensions
    {
        public static PropertyInfo[] GetPublicProperties(this Type type)
        {
            return type.GetPublicProperties(BindingFlags.Default);
        }

        internal static PropertyInfo[] GetPublicProperties(this Type type, BindingFlags bindingFlags)
        {
            if (type.IsInterface)
            {
                var propertyInfos = new List<PropertyInfo>();

                var considered = new List<Type>();
                var queue = new Queue<Type>();
                considered.Add(type);
                queue.Enqueue(type);
                while (queue.Count > 0)
                {
                    var subType = queue.Dequeue();
                    foreach (var subInterface in subType.GetInterfaces())
                    {
                        if (considered.Contains(subInterface)) continue;

                        considered.Add(subInterface);
                        queue.Enqueue(subInterface);
                    }

                    var typeProperties = subType.GetProperties(
                        BindingFlags.FlattenHierarchy
                        | BindingFlags.Public
                        | BindingFlags.Instance
                        | bindingFlags);

                    var newPropertyInfos = typeProperties
                        .Where(x => !propertyInfos.Contains(x));

                    propertyInfos.InsertRange(0, newPropertyInfos);
                }

                return propertyInfos.ToArray();
            }

            return type.GetProperties(BindingFlags.FlattenHierarchy
                | BindingFlags.Public | BindingFlags.Instance
                | bindingFlags);
        }

        public static void CopyTo<T>(this T source, T target)
        {
            ClassCopier<T>.Copy(source, target);
        }

        /// <summary>
        /// 将某个对象的属性值拷贝到子类的对象的对应属性上
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TDestination"></typeparam>
        /// <param name="source"></param>
        /// <param name="destination"></param>
        public static void CopyToSubclass<TSource, TDestination>(this TSource source, TDestination destination)
            where TDestination : TSource
        {
            if (destination == null)
                throw new ArgumentNullException("destination");

            Type t = typeof(TSource);
            var props = t.GetPublicProperties(BindingFlags.SetField | BindingFlags.SetProperty);
            foreach (var prop in props)
            {
                if (prop.CanWrite)
                    prop.SetValue(destination, prop.GetValue(source, null), null);
            }
        }

        /// <summary>
        /// 将某个对象的基类属性值拷贝到基类对象的对应的属性上
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TDestination"></typeparam>
        /// <param name="source"></param>
        /// <param name="destination"></param>
        public static void CopyToBase<TSource, TDestination>(this TSource source, TDestination destination)
            where TSource : TDestination
        {
            if (destination == null)
                throw new ArgumentNullException("destination");

            Type t = typeof(TDestination);
            var props = t.GetPublicProperties(BindingFlags.SetField | BindingFlags.SetProperty);
            foreach (var prop in props)
            {
                if (prop.CanWrite)
                    prop.SetValue(destination, prop.GetValue(source, null), null);
            }
        }

        public static TDestination CloneToSubclass<TSource, TDestination>(this TSource source)
            where TDestination : TSource, new()
        {
            TDestination destination = new TDestination();

            source.CopyToSubclass(destination);

            return destination;
        }
    }
}
